package com.feizhi.common.search.index;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;

import org.aspectj.lang.JoinPoint;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;

import com.feizhi.common.OperateResult;
import com.feizhi.common.reflect.ClassUtils;
import com.feizhi.common.search.index.action.AddIndexAction;
import com.feizhi.common.search.index.action.DeleteIndexAction;
import com.feizhi.common.search.index.action.FlushIndexAction;
import com.feizhi.common.search.index.action.IndexAction;
import com.feizhi.common.search.index.action.UpdateIndexAction;
import com.feizhi.common.search.index.annotation.IndexTarget;
import com.feizhi.common.search.index.annotation.Indexed;
import com.feizhi.common.search.index.annotation.LuceneWorkHelperBean;

public class IndexAdvice {

    private ApplicationContext context;

    private AddIndexAction addIndexAction;

    private UpdateIndexAction updateIndexAction;

    private DeleteIndexAction deleteIndexAction;

    private FlushIndexAction flushIndexAction;

    @Autowired
    public void setContext(final ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setAddIndexAction(final AddIndexAction addIndexAction) {
        this.addIndexAction = addIndexAction;
    }

    @Autowired
    public void setUpdateIndexAction(final UpdateIndexAction updateIndexAction) {
        this.updateIndexAction = updateIndexAction;
    }

    @Autowired
    public void setDeleteIndexAction(final DeleteIndexAction deleteIndexAction) {
        this.deleteIndexAction = deleteIndexAction;
    }

    @Autowired
    public void setFlushIndexAction(final FlushIndexAction flushIndexAction) {
        this.flushIndexAction = flushIndexAction;
    }

    public <B> boolean addIndex(final OperateResult result, final B bean) {
        if (!result.isOk() || bean == null) {
            return false;
        }

        return performAction(addIndexAction, bean);
    }

    public boolean updateIndex(final OperateResult result, final Object bean) {
        if (!result.isOk() || bean == null) {
            return false;
        }

        return performAction(updateIndexAction, bean);
    }

    private <B> boolean performAction(final IndexAction action, final B bean) {
        final LuceneWork work = buildWorkFromBean(bean);
        if (work == null) {
            // 不能够构造对应的work.
            return false;
        }
        action.performAction(work);
        flushIndexAction.performAction(null);
        return true;
    }

    private <B> LuceneWork buildWorkFromBean(final B bean) {
        final Class<? extends Object> beanClass = bean.getClass();

        // 如果定义了@LuceneWorkHelperBean 的Annotation, 则优先使用这个Annotation 来获取目标的LuceneWorkHelper
        final LuceneWorkHelperBean helperAnnotation = beanClass
                .getAnnotation(LuceneWorkHelperBean.class);
        if (helperAnnotation != null) {
            final LuceneWork work = buildWorkByHelper(helperAnnotation, bean);
            if (work != null) {
                return work;
            }
        }

        // 如果没有定义@LuceneWorkHelperBean 的Annotation, 则尝试看是否有@Indexed 的Annotation
        // 如果有, 则再结合@DocumentId 和@Field (@Fields) 来组织work
        final Indexed indexedAnnotation = beanClass.getAnnotation(Indexed.class);
        if (indexedAnnotation != null) {
            return buildWorkByAnnotation(indexedAnnotation, beanClass, bean);
        }

        // 如果都没有, 那没办法了吧.
        return null;
    }

    private <B> LuceneWork buildWorkByHelper(final LuceneWorkHelperBean helperAnnotation,
            final B bean) {
        final LuceneWorkHelper<B> luceneWorkHelper = getLuceneWorkHelper(helperAnnotation);
        if (luceneWorkHelper != null) {
            return luceneWorkHelper.toWork(bean);
        }
        return null;
    }

    private <B> LuceneWorkHelper<B> getLuceneWorkHelper(final LuceneWorkHelperBean annotation) {
        final String beanName = annotation.value();
        if (StringUtils.hasText(beanName)) {
            try {
                @SuppressWarnings("unchecked")
                final LuceneWorkHelper<B> helper = context
                        .getBean(beanName, LuceneWorkHelper.class);
                return helper;
            } catch (final BeansException e) {
            }
        }

        @SuppressWarnings("rawtypes")
        final Class<? extends LuceneWorkHelper> type = annotation.type();
        if (type != null && type != LuceneWorkHelper.class) {
            try {
                @SuppressWarnings("unchecked")
                final LuceneWorkHelper<B> helper = context.getBean(type);
                return helper;
            } catch (final BeansException e) {
            }
        }
        return null;
    }

    private <B> LuceneWork buildWorkByAnnotation(final Indexed indexedAnnotation,
            final Class<? extends Object> beanClass, final B bean) {
        final LuceneWork work = new LuceneWork();
        work.setEntityType(beanClass);

        final String id = indexedAnnotation.id();
        final Serializable idValue = getIdValue(beanClass, id, bean);
        if (idValue == null) {
            return null;
        }
        work.setId(idValue);

        // final Field[] fields = beanClass.getDeclaredFields();
        // for (final Field field : fields) {
        // }
        return work;
    }

    private <B> Serializable getIdValue(final Class<? extends Object> beanClass, final String id,
            final B bean) {
        try {
            final Field idField = beanClass.getField(id);
            final Object value = idField.get(bean);
            final Serializable idValue = (Serializable) value;
            return idValue;
        } catch (final Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public boolean deleteIndex(final JoinPoint joinPoint, final OperateResult result,
            final Serializable id) {
        if (!result.isOk()) {
            return false;
        }

        final Class<?> beanClass = getBeanClass(joinPoint);
        if (beanClass == null) {
            // 找不到对应的Bean Class
            return false;
        }

        final LuceneWork work = new LuceneWork();
        work.setEntityType(beanClass);
        work.setId(id);

        deleteIndexAction.performAction(work);
        flushIndexAction.performAction(null);
        return true;
    }

    private Class<?> getBeanClass(final JoinPoint joinPoint) {
        final Class<? extends Object> targetClass = joinPoint.getTarget().getClass();

        // 如果定义了Annotation, 则优先使用Annotation 来获取目标的Bean Class
        final IndexTarget annotation = targetClass.getAnnotation(IndexTarget.class);
        if (annotation != null) {
            return annotation.value();
        }

        // 如果没有定义Annotation, 则尝试看是否继承了ResourceService<T> 这样的接口, 如果有, 则获取这里T 所对应的具体的Class.
        // 比如: class XxxService implements ResourceService<Xxx> {} 这里面, 尝试获取Xxx 这个类
        final Type[] types = ClassUtils.getParameterizedTypes(targetClass);
        if (types != null && types.length > 0) {
            return (Class<?>) types[0];
        }

        // 如果都没有, 那没办法了吧.
        return null;
    }
}
